package com.nttdata.ta.todo.service.impl;

import com.nttdata.ta.common.dto.ResponseResult;
import com.nttdata.ta.common.dto.TodoDTO;
import com.nttdata.ta.common.entity.TodoItem;
import com.nttdata.ta.todo.feign.UserServiceClient;
import com.nttdata.ta.todo.repository.TodoItemRepository;
import com.nttdata.ta.todo.service.TodoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class TodoServiceImpl implements TodoService {
    
    private final TodoItemRepository todoItemRepository;
    private final UserServiceClient userServiceClient;
    
    // 从配置文件中获取缓存有效期设置
    @Value("${app.cache.todo-list-ttl:60}")
    private int todoListTtl; // 默认60秒
    
    @Value("${app.cache.todo-item-ttl:300}")
    private int todoItemTtl; // 默认300秒
    
    @Value("${app.cache.user-id-ttl:10}")
    private int userIdTtl; // 默认10分钟
    
    @Value("${app.cache.token-ttl:10}")
    private int tokenTtl; // 默认10分钟
    
    @Value("${app.cache.cleanup-interval:5}")
    private int cleanupInterval; // 默认5分钟
    
    // 用户待办事项缓存 - Map<用户ID, List<待办事项>>
    private final Map<Long, TodoListCacheEntry> userTodosCache = new ConcurrentHashMap<>();
    
    // 单个待办事项缓存 - Map<待办事项ID, 待办事项>
    private final Map<Long, TodoDTO> todoCache = new ConcurrentHashMap<>();
    
    // 用户ID缓存 - Map<Token, 用户ID>
    private final Map<String, CacheEntry<Long>> userIdCache = new ConcurrentHashMap<>();
    
    // 令牌验证缓存 - Map<Token, 是否有效>
    private final Map<String, CacheEntry<Boolean>> tokenValidationCache = new ConcurrentHashMap<>();
    
    // 定时清理缓存的调度器
    private final ScheduledExecutorService cacheCleanupScheduler = Executors.newScheduledThreadPool(1);
    
    @PostConstruct
    public void init() {
        // 在Bean初始化后启动缓存清理任务
        cacheCleanupScheduler.scheduleAtFixedRate(
            this::cleanupExpiredCache, 
            cleanupInterval, 
            cleanupInterval, 
            TimeUnit.MINUTES
        );
        log.info("TodoService缓存清理任务已启动，清理间隔: {}分钟", cleanupInterval);
        log.info("缓存有效期配置 - 待办列表: {}秒, 待办项: {}秒, 用户ID: {}分钟, 令牌: {}分钟", 
            todoListTtl, todoItemTtl, userIdTtl, tokenTtl);
    }
    
    @Override
    public List<TodoDTO> getAllTodosByUserId(Long userId) {
        if (userId == null) {
            log.info("获取所有待办事项");
            return todoItemRepository.findAll().stream()
                    .map(this::convertToDTO)
                    .collect(Collectors.toList());
        }
        
        // 检查缓存
        TodoListCacheEntry cacheEntry = userTodosCache.get(userId);
        if (cacheEntry != null && !cacheEntry.isExpired()) {
            log.debug("从缓存获取用户 {} 的待办事项列表", userId);
            return cacheEntry.getTodos();
        }
        
        log.info("从数据库获取用户ID: {} 的所有待办事项", userId);
        List<TodoDTO> todos = todoItemRepository.findByUserId(userId).stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        // 更新缓存，使用配置的过期时间
        userTodosCache.put(userId, new TodoListCacheEntry(todos, 
            LocalDateTime.now().plusSeconds(todoListTtl)));
        
        // 同时更新单个待办事项缓存
        todos.forEach(todo -> todoCache.put(todo.getId(), todo));
        
        return todos;
    }
    
    @Override
    public TodoDTO getTodoById(Long id) {
        // 检查缓存
        TodoDTO cachedTodo = todoCache.get(id);
        if (cachedTodo != null) {
            log.debug("从缓存获取待办事项 {}", id);
            return cachedTodo;
        }
        
        log.info("从数据库获取待办事项ID: {}", id);
        TodoDTO todo = todoItemRepository.findById(id)
                .map(this::convertToDTO)
                .orElseThrow(() -> new RuntimeException("待办事项不存在"));
        
        // 更新缓存
        todoCache.put(id, todo);
        
        return todo;
    }
    
    @Override
    @Transactional
    public TodoDTO createTodo(TodoDTO todoDTO) {
        log.info("创建待办事项: {}", todoDTO.getName());
        
        TodoItem todoItem = new TodoItem();
        todoItem.setName(todoDTO.getName());
        todoItem.setCategory(todoDTO.getCategory());
        todoItem.setComplete(todoDTO.isComplete());
        todoItem.setUserId(todoDTO.getUserId());
        
        TodoItem savedItem = todoItemRepository.save(todoItem);
        log.info("待办事项创建成功: {}", savedItem.getId());
        
        TodoDTO createdTodo = convertToDTO(savedItem);
        
        // 更新缓存
        todoCache.put(createdTodo.getId(), createdTodo);
        invalidateUserTodoListCache(todoDTO.getUserId());
        
        return createdTodo;
    }
    
    @Override
    @Transactional
    public TodoDTO updateTodo(Long id, TodoDTO todoDTO) {
        log.info("更新待办事项ID: {}", id);
        
        TodoItem todoItem = todoItemRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("待办事项不存在"));
        
        if (todoDTO.getName() != null) {
            todoItem.setName(todoDTO.getName());
        }
        
        if (todoDTO.getCategory() != null) {
            todoItem.setCategory(todoDTO.getCategory());
        }
        
        todoItem.setComplete(todoDTO.isComplete());
        
        TodoItem updatedItem = todoItemRepository.save(todoItem);
        log.info("待办事项更新成功: {}", updatedItem.getId());
        
        TodoDTO updatedTodo = convertToDTO(updatedItem);
        
        // 更新缓存
        todoCache.put(updatedTodo.getId(), updatedTodo);
        invalidateUserTodoListCache(updatedItem.getUserId());
        
        return updatedTodo;
    }
    
    @Override
    @Transactional
    public void deleteTodo(Long id) {
        log.info("删除待办事项ID: {}", id);
        
        // 在删除前获取待办事项，以便后续清除用户的待办列表缓存
        TodoItem todoItem = todoItemRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("待办事项不存在"));
        Long userId = todoItem.getUserId();
        
        todoItemRepository.deleteById(id);
        log.info("待办事项删除成功: {}", id);
        
        // 更新缓存
        todoCache.remove(id);
        invalidateUserTodoListCache(userId);
    }
    
    @Override
    public Long getUserIdFromToken(String token) {
        // 检查缓存
        CacheEntry<Long> cacheEntry = userIdCache.get(token);
        if (cacheEntry != null && !cacheEntry.isExpired()) {
            log.debug("从缓存获取令牌的用户ID");
            return cacheEntry.getValue();
        }
        
        try {
            log.info("通过Feign调用获取令牌中的用户ID");
            ResponseResult<Long> result = userServiceClient.getUserIdFromToken("Bearer " + token);
            Long userId = result.getData();
            
            // 更新缓存，使用配置的过期时间
            userIdCache.put(token, new CacheEntry<>(userId, 
                LocalDateTime.now().plusMinutes(userIdTtl)));
            
            return userId;
        } catch (Exception e) {
            log.error("获取令牌中的用户ID失败", e);
            throw new RuntimeException("无效的令牌");
        }
    }
    
    @Override
    public boolean validateToken(String token) {
        // 检查缓存
        CacheEntry<Boolean> cacheEntry = tokenValidationCache.get(token);
        if (cacheEntry != null && !cacheEntry.isExpired()) {
            log.debug("从缓存获取令牌验证结果");
            return cacheEntry.getValue();
        }
        
        try {
            log.info("通过Feign调用验证令牌");
            ResponseResult<Boolean> result = userServiceClient.validateToken("Bearer " + token);
            boolean isValid = result.getData() != null && result.getData();
            
            // 更新缓存，使用配置的过期时间
            tokenValidationCache.put(token, new CacheEntry<>(isValid, 
                LocalDateTime.now().plusMinutes(tokenTtl)));
            
            return isValid;
        } catch (Exception e) {
            log.error("令牌验证失败", e);
            return false;
        }
    }
    
    private TodoDTO convertToDTO(TodoItem todoItem) {
        return new TodoDTO(
                todoItem.getId(),
                todoItem.getCategory(),
                todoItem.getName(),
                todoItem.isComplete(),
                todoItem.getUserId()
        );
    }
    
    /**
     * 清除用户待办列表的缓存
     */
    private void invalidateUserTodoListCache(Long userId) {
        userTodosCache.remove(userId);
        log.debug("已清除用户 {} 的待办列表缓存", userId);
    }
    
    /**
     * 定期清理过期的缓存
     */
    private void cleanupExpiredCache() {
        log.debug("开始清理过期缓存...");
        
        // 清理用户待办事项缓存
        int todoListCacheSizeBefore = userTodosCache.size();
        userTodosCache.entrySet().removeIf(entry -> entry.getValue().isExpired());
        int todoListCacheSizeAfter = userTodosCache.size();
        
        // 清理用户ID缓存
        int userIdCacheSizeBefore = userIdCache.size();
        userIdCache.entrySet().removeIf(entry -> entry.getValue().isExpired());
        int userIdCacheSizeAfter = userIdCache.size();
        
        // 清理令牌验证缓存
        int tokenCacheSizeBefore = tokenValidationCache.size();
        tokenValidationCache.entrySet().removeIf(entry -> entry.getValue().isExpired());
        int tokenCacheSizeAfter = tokenValidationCache.size();
        
        log.debug("缓存清理完成: 待办列表缓存 -{}, 用户ID缓存 -{}, 令牌验证缓存 -{}", 
            todoListCacheSizeBefore - todoListCacheSizeAfter,
            userIdCacheSizeBefore - userIdCacheSizeAfter,
            tokenCacheSizeBefore - tokenCacheSizeAfter);
    }
    
    /**
     * 通用缓存条目类
     */
    private static class CacheEntry<T> {
        private final T value;
        private final LocalDateTime expirationTime;
        
        public CacheEntry(T value, LocalDateTime expirationTime) {
            this.value = value;
            this.expirationTime = expirationTime;
        }
        
        public T getValue() {
            return value;
        }
        
        public boolean isExpired() {
            return LocalDateTime.now().isAfter(expirationTime);
        }
    }
    
    /**
     * 待办事项列表缓存条目
     */
    private static class TodoListCacheEntry {
        private final List<TodoDTO> todos;
        private final LocalDateTime expirationTime;
        
        public TodoListCacheEntry(List<TodoDTO> todos, LocalDateTime expirationTime) {
            this.todos = todos;
            this.expirationTime = expirationTime;
        }
        
        public List<TodoDTO> getTodos() {
            return todos;
        }
        
        public boolean isExpired() {
            return LocalDateTime.now().isAfter(expirationTime);
        }
    }
} 